#pragma once

#include "Operation.h"

#include "Class.h"
#include "Constraint.h"
#include "DataType.h"
#include "Interface.h"
#include "OperationTemplateParameter.h"
#include "Parameter.h"
#include "RedefinableElement.h"
#include "Type.h"

namespace uml {

const bool Operation::isOrdered_default_ = false;
const bool Operation::isQuery_default_ = false;
const bool Operation::isUnique_default_ = false;
const int Operation::lower_default_ = 0;
const int Operation::upper_default_ = 0;
Operation::Operation(const QString& umlType)
    : BehavioralFeature(umlType) {}

void Operation::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    ParameterableElement::fromJson(json, errorList);
    BehavioralFeature::fromJson(json, errorList);
    TemplateableElement::fromJson(json, errorList);
    bodyCondition_ = JsonSerializable::create<Constraint>(json.value("bodyCondition"), errorList);

    class2_ = JsonSerializable::create<Class>(json.value("class2"), errorList);

    datatype_ = JsonSerializable::create<DataType>(json.value("datatype"), errorList);

    interface2_ = JsonSerializable::create<Interface>(json.value("interface2"), errorList);

    isOrdered_ = json.value("isOrdered").toBool(isOrdered_default_);
    isQuery_ = json.value("isQuery").toBool(isQuery_default_);
    isUnique_ = json.value("isUnique").toBool(isUnique_default_);
    lower_ = json.value("lower").toInt(lower_default_);
    JsonSerializable::fromJsonArray(ownedParameters_, "ownedParameters", json, errorList);
    JsonSerializable::fromJsonArray(postconditions_, "postconditions", json, errorList);
    JsonSerializable::fromJsonArray(preconditions_, "preconditions", json, errorList);
    JsonSerializable::fromJsonArray(raisedExceptions_, "raisedExceptions", json, errorList);
    JsonSerializable::fromJsonArray(redefinedOperations_, "redefinedOperations", json, errorList);
    templateParameter_ = JsonSerializable::create<OperationTemplateParameter>(json.value("templateParameter"), errorList);

    type_ = JsonSerializable::create<Type>(json.value("type"), errorList);

    upper_ = json.value("upper").toInt(upper_default_);
}

void Operation::toJson(QJsonObject& json) {
    ParameterableElement::toJson(json);
    BehavioralFeature::toJson(json);
    TemplateableElement::toJson(json);
    if (bodyCondition_) {
        QJsonObject bodyCondition_json;
        bodyCondition_->toJson(bodyCondition_json);
        json.insert("bodyCondition", bodyCondition_json);
    }
    if (class2_) {
        QJsonObject class2_json;
        class2_->toJson(class2_json);
        json.insert("class2", class2_json);
    }
    if (datatype_) {
        QJsonObject datatype_json;
        datatype_->toJson(datatype_json);
        json.insert("datatype", datatype_json);
    }
    if (interface2_) {
        QJsonObject interface2_json;
        interface2_->toJson(interface2_json);
        json.insert("interface2", interface2_json);
    }
    if (isOrdered_ != isOrdered_default_) {
        json.insert("isOrdered", isOrdered_);
    }
    if (isQuery_ != isQuery_default_) {
        json.insert("isQuery", isQuery_);
    }
    if (isUnique_ != isUnique_default_) {
        json.insert("isUnique", isUnique_);
    }
    if (lower_ != lower_default_) {
        json.insert("lower", lower_);
    }
    JsonSerializable::toJsonArray(ownedParameters_, "ownedParameters", json);
    JsonSerializable::toJsonArray(postconditions_, "postconditions", json);
    JsonSerializable::toJsonArray(preconditions_, "preconditions", json);
    JsonSerializable::toJsonArray(raisedExceptions_, "raisedExceptions", json);
    JsonSerializable::toJsonArray(redefinedOperations_, "redefinedOperations", json);
    if (templateParameter_) {
        QJsonObject templateParameter_json;
        templateParameter_->toJson(templateParameter_json);
        json.insert("templateParameter", templateParameter_json);
    }
    if (type_) {
        QJsonObject type_json;
        type_->toJson(type_json);
        json.insert("type", type_json);
    }
    if (upper_ != upper_default_) {
        json.insert("upper", upper_);
    }
}

/// <summary>
/// isConsistentWith
    /// The query isConsistentWith() specifies, for any two Operations in a context in
    /// which redefinition is possible, whether redefinition would be consistent. A
    /// redefining operation is consistent with a redefined operation if
    /// it has
    /// the same number of owned parameters, and for each parameter the following
    /// holds:
    /// 
    /// - Direction, ordering and uniqueness are the same.
   
    /// /// - The corresponding types are covariant, contravariant or invariant.
    ///
    /// - The multiplicities are compatible, depending on the parameter direction.
/// </summary>
    /// <code>
    /// result = (redefiningElement.oclIsKindOf(Operation) and
    /// let op : Operation = redefiningElement.oclAsType(Operation) in
    /// 	self.ownedParameter->size() = op.ownedParameter->size() and
    /// 	Sequence{1..self.ownedParameter->size()}->
    /// 		forAll(i |  
    /// 		  let redefiningParam : Parameter = op.ownedParameter->at(i),
    ///                redefinedParam : Parameter = self.ownedParameter->at(i) in
    ///                  (redefiningParam.isUnique = redefinedParam.isUnique) and
    ///                  (redefiningParam.isOrdered = redefinedParam. isOrdered) and
    ///                  (redefiningParam.direction = redefinedParam.direction) and
    ///                  (redefiningParam.type.conformsTo(redefinedParam.type) or
    ///                      redefinedParam.type.conformsTo(redefiningParam.type)) and
    ///                  (redefiningParam.direction = ParameterDirectionKind::inout implies
    ///                          (redefinedParam.compatibleWith(redefiningParam) and
    ///                          redefiningParam.compatibleWith(redefinedParam))) and
    ///                  (redefiningParam.direction = ParameterDirectionKind::_'in' implies
    ///                          redefinedParam.compatibleWith(redefiningParam)) and
    ///                  ((redefiningParam.direction = ParameterDirectionKind::out or
    ///                       redefiningParam.direction = ParameterDirectionKind::return) implies
    ///                          redefiningParam.compatibleWith(redefinedParam))
    /// 		))
    /// </code>
    /// <code>
    /// redefiningElement.isRedefinitionContextValid(self)
    /// </code>
bool Operation::isConsistentWith(shared_ptr<RedefinableElement> redefiningElement) const
{
    return false;
}

/// <summary>
/// returnResults
    /// The query returnResult() returns the set containing the return parameter of the
    /// Operation if one exists, otherwise, it returns an empty set
/// </summary>
    /// <code>
    /// result = (ownedParameter->select (direction = ParameterDirectionKind::return))
    /// </code>
shared_ptr<vector<Parameter>> Operation::returnResults() const
{
    return nullptr;
}

/// <summary>
/// bodyCondition getter
/// An optional Constraint on the result values of an invocation of this Operation.
/// </summary>
shared_ptr<Constraint> Operation::bodyCondition() const {
    return bodyCondition_;
}

/// <summary>
/// bodyCondition setter
/// An optional Constraint on the result values of an invocation of this Operation.
/// </summary>
void Operation::setBodyCondition(shared_ptr<Constraint> value) {
    bodyCondition_ = value;
}

/// <summary>
/// class2 getter
/// The Class that owns this operation, if any.
/// </summary>
shared_ptr<Class> Operation::class2() const {
    return class2_;
}

/// <summary>
/// class2 setter
/// The Class that owns this operation, if any.
/// </summary>
void Operation::setClass2(shared_ptr<Class> value) {
    class2_ = value;
}

/// <summary>
/// datatype getter
/// The DataType that owns this Operation, if any.
/// </summary>
shared_ptr<DataType> Operation::datatype() const {
    return datatype_;
}

/// <summary>
/// datatype setter
/// The DataType that owns this Operation, if any.
/// </summary>
void Operation::setDatatype(shared_ptr<DataType> value) {
    datatype_ = value;
}

/// <summary>
/// interface2 getter
/// The Interface that owns this Operation, if any.
/// </summary>
shared_ptr<Interface> Operation::interface2() const {
    return interface2_;
}

/// <summary>
/// interface2 setter
/// The Interface that owns this Operation, if any.
/// </summary>
void Operation::setInterface2(shared_ptr<Interface> value) {
    interface2_ = value;
}

/// <summary>
/// isOrdered getter
/// Specifies whether the return parameter is ordered or not, if present.  This
/// information is derived from the return result for this Operation.
/// </summary>
bool Operation::isOrdered() const {
    return isOrdered_;
}

/// <summary>
/// isOrdered setter
/// Specifies whether the return parameter is ordered or not, if present.  This
/// information is derived from the return result for this Operation.
/// </summary>
void Operation::setIsOrdered(bool value) {
    isOrdered_ = value;
}

/// <summary>
/// isQuery getter
/// Specifies whether an execution of the BehavioralFeature leaves the state of the
/// system unchanged (isQuery=true) or whether side effects may occur
/// (isQuery=false).
/// </summary>
bool Operation::isQuery() const {
    return isQuery_;
}

/// <summary>
/// isQuery setter
/// Specifies whether an execution of the BehavioralFeature leaves the state of the
/// system unchanged (isQuery=true) or whether side effects may occur
/// (isQuery=false).
/// </summary>
void Operation::setIsQuery(bool value) {
    isQuery_ = value;
}

/// <summary>
/// isUnique getter
/// Specifies whether the return parameter is unique or not, if present. This
/// information is derived from the return result for this Operation.
/// </summary>
bool Operation::isUnique() const {
    return isUnique_;
}

/// <summary>
/// isUnique setter
/// Specifies whether the return parameter is unique or not, if present. This
/// information is derived from the return result for this Operation.
/// </summary>
void Operation::setIsUnique(bool value) {
    isUnique_ = value;
}

/// <summary>
/// lower getter
/// Specifies the lower multiplicity of the return parameter, if present. This
/// information is derived from the return result for this Operation.
/// </summary>
int Operation::lower() const {
    return lower_;
}

/// <summary>
/// lower setter
/// Specifies the lower multiplicity of the return parameter, if present. This
/// information is derived from the return result for this Operation.
/// </summary>
void Operation::setLower(int value) {
    lower_ = value;
}

/// <summary>
/// ownedParameters getter
/// The parameters owned by this Operation.
/// </summary>
vector<shared_ptr<Parameter>>& Operation::ownedParameters() {
    return ownedParameters_;
}

/// <summary>
/// postconditions getter
/// An optional set of Constraints specifying the state of the system when the
/// Operation is completed.
/// </summary>
vector<shared_ptr<Constraint>>& Operation::postconditions() {
    return postconditions_;
}

/// <summary>
/// preconditions getter
/// An optional set of Constraints on the state of the system when the Operation is
/// invoked.
/// </summary>
vector<shared_ptr<Constraint>>& Operation::preconditions() {
    return preconditions_;
}

/// <summary>
/// raisedExceptions getter
/// The Types representing exceptions that may be raised during an invocation of
/// this operation.
/// </summary>
vector<shared_ptr<Type>>& Operation::raisedExceptions() {
    return raisedExceptions_;
}

/// <summary>
/// redefinedOperations getter
/// The Operations that are redefined by this Operation.
/// </summary>
vector<shared_ptr<Operation>>& Operation::redefinedOperations() {
    return redefinedOperations_;
}

/// <summary>
/// templateParameter getter
/// The OperationTemplateParameter that exposes this element as a formal parameter.
/// </summary>
shared_ptr<OperationTemplateParameter> Operation::templateParameter() const {
    return templateParameter_;
}

/// <summary>
/// templateParameter setter
/// The OperationTemplateParameter that exposes this element as a formal parameter.
/// </summary>
void Operation::setTemplateParameter(shared_ptr<OperationTemplateParameter> value) {
    templateParameter_ = value;
}

/// <summary>
/// type getter
/// The return type of the operation, if present. This information is derived from
/// the return result for this Operation.
/// </summary>
shared_ptr<Type> Operation::type() const {
    return type_;
}

/// <summary>
/// type setter
/// The return type of the operation, if present. This information is derived from
/// the return result for this Operation.
/// </summary>
void Operation::setType(shared_ptr<Type> value) {
    type_ = value;
}

/// <summary>
/// upper getter
/// The upper multiplicity of the return parameter, if present. This information is
/// derived from the return result for this Operation.
/// </summary>
int Operation::upper() const {
    return upper_;
}

/// <summary>
/// upper setter
/// The upper multiplicity of the return parameter, if present. This information is
/// derived from the return result for this Operation.
/// </summary>
void Operation::setUpper(int value) {
    upper_ = value;
}

} // namespace uml
